home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / HippoDraw / HippoDrawSrc1.1 / Hippo.subproj / Line.m < prev    next >
Encoding:
Text File  |  1992-04-25  |  6.6 KB  |  281 lines

  1. #import "Line.h"
  2. #import "draw.h"
  3. #import <math.h>
  4.  
  5. @implementation Line : Graphic
  6. /*
  7.  * Drawing a line is simple except that we have to keep track of whether
  8.  * the line goes from the upper left to the lower right of the bounds or
  9.  * from the lower left to the upper right.  This can easily be determined
  10.  * every time a corner is moved to a different corner.  Therefore, all
  11.  * that is needed is to override moveCorner:to:constrain: to keep track
  12.  * of that.  It is an efficiency hack to have the downhill flag kept
  13.  * in our superclass's flags.
  14.  */
  15.  
  16. #define HIT_TOLERANCE 6.0
  17.  
  18. + initialize
  19. {
  20.     [self setVersion:1];
  21.     return self;
  22. }
  23.  
  24. - init
  25. {
  26.     [super init];
  27.     startCorner = LOWER_LEFT;
  28.     return self;
  29. }
  30.  
  31. - (BOOL)isValid
  32. /*
  33.  * A line is validly created if EITHER of the dimensions is big enough.
  34.  */
  35. {
  36.     return(bounds.size.width >= 5.0 || bounds.size.height >= 5.0);
  37. }
  38.  
  39. static int oppositeCorner(int corner)
  40. {
  41.     switch (corner) {
  42.     case UPPER_RIGHT: return LOWER_LEFT;
  43.     case LOWER_LEFT: return UPPER_RIGHT;
  44.     case UPPER_LEFT: return LOWER_RIGHT;
  45.     case LOWER_RIGHT: return UPPER_LEFT;
  46.     }
  47.  
  48.     return corner;
  49. }
  50.  
  51. - (int)moveCorner:(int)corner to:(const NXPoint *)point constrain:(BOOL)flag
  52. /*
  53.  * Moves the corner to the specified point keeping track of whether the
  54.  * line is going uphill or downhill and where the start corner has moved to.
  55.  */
  56. {
  57.     int newcorner;
  58.  
  59.     newcorner = [super moveCorner:corner to:point constrain:flag];
  60.  
  61.     if (newcorner != corner) {
  62.     if ((newcorner == UPPER_RIGHT && corner == LOWER_LEFT) ||
  63.         (newcorner == UPPER_LEFT && corner == LOWER_RIGHT) ||
  64.         (newcorner == LOWER_RIGHT && corner == UPPER_LEFT) ||
  65.         (newcorner == LOWER_LEFT && corner == UPPER_RIGHT)) {
  66.     } else {
  67.         gFlags.downhill = !gFlags.downhill;
  68.     }
  69.     if (startCorner == corner) {
  70.         startCorner = newcorner;
  71.     } else {
  72.         startCorner = oppositeCorner(newcorner);
  73.     }
  74.     }
  75.  
  76.     return newcorner;
  77. }
  78.  
  79. - constrainCorner:(int)corner toAspectRatio:(float)ratio
  80. /*
  81.  * Constrains the corner to the nearest 15 degree angle.  Ignores ratio.
  82.  */
  83. {
  84.     NXCoord width, height;
  85.     double angle, distance;
  86.  
  87.     distance = hypot(bounds.size.width, bounds.size.height);
  88.     angle = atan2(bounds.size.height, bounds.size.width);
  89.     angle = (angle / 3.1415) * 180.0;
  90.     angle = floor(angle / 15.0 + 0.5) * 15.0;
  91.     angle = (angle / 180.0) * 3.1415;
  92.     width = floor(cos(angle) * distance + 0.5);
  93.     height = floor(sin(angle) * distance + 0.5);
  94.  
  95.     switch (corner) {
  96.     case LOWER_LEFT:
  97.         bounds.origin.x -= width - bounds.size.width;
  98.         bounds.origin.y -= height - bounds.size.height;
  99.         break;
  100.     case UPPER_LEFT:
  101.         bounds.origin.x -= width - bounds.size.width;
  102.         break;
  103.     case LOWER_RIGHT:
  104.         bounds.origin.y -= height - bounds.size.height;
  105.         break;
  106.     }
  107.  
  108.     bounds.size.width = width;
  109.     bounds.size.height = height;
  110.  
  111.     return self;
  112. }
  113.  
  114. - (int)cornerMask
  115. /*
  116.  * Only put corner knobs at the start and end of the line.
  117.  */
  118. {
  119.     if (gFlags.downhill) {
  120.     return(UPPER_LEFT_MASK|LOWER_RIGHT_MASK);
  121.     } else {
  122.     return(LOWER_LEFT_MASK|UPPER_RIGHT_MASK);
  123.     }
  124. }
  125.  
  126. - draw
  127. /*
  128.  * Calls drawLine to draw the line, then draws the arrows if any.
  129.  */
  130. {
  131.     if (bounds.size.width < 1.0 && bounds.size.height < 1.0) return self;
  132.  
  133.     [self setLineColor];
  134.     [self drawLine];
  135.  
  136.     if (gFlags.arrow) {
  137.     if (gFlags.downhill) {
  138.         if (((gFlags.arrow != ARROW_AT_START) &&
  139.              (startCorner == LOWER_RIGHT)) ||
  140.         ((gFlags.arrow != ARROW_AT_END) &&
  141.          (startCorner == UPPER_LEFT))) {
  142.         PSArrow(bounds.origin.x,
  143.             bounds.origin.y + bounds.size.height,
  144.             [self arrowAngle:UPPER_LEFT]);        
  145.         }
  146.         if (((gFlags.arrow != ARROW_AT_START) &&
  147.              (startCorner == UPPER_LEFT)) ||
  148.         ((gFlags.arrow != ARROW_AT_END) &&
  149.          (startCorner == LOWER_RIGHT))) {
  150.         PSArrow(bounds.origin.x + bounds.size.width,
  151.             bounds.origin.y,
  152.             [self arrowAngle:LOWER_RIGHT]);        
  153.         }
  154.     } else {
  155.         if (((gFlags.arrow != ARROW_AT_START) &&
  156.              (startCorner == LOWER_LEFT)) ||
  157.         ((gFlags.arrow != ARROW_AT_END) &&
  158.          (startCorner == UPPER_RIGHT))) {
  159.         PSArrow(bounds.origin.x + bounds.size.width,
  160.             bounds.origin.y + bounds.size.height,
  161.             [self arrowAngle:UPPER_RIGHT]);        
  162.         }
  163.         if (((gFlags.arrow != ARROW_AT_START) &&
  164.              (startCorner == UPPER_RIGHT)) ||
  165.         ((gFlags.arrow != ARROW_AT_END) &&
  166.          (startCorner == LOWER_LEFT))) {
  167.         PSArrow(bounds.origin.x,
  168.             bounds.origin.y,
  169.             [self arrowAngle:LOWER_LEFT]);        
  170.         }
  171.     }
  172.     }
  173.  
  174.     return self;
  175. }
  176.  
  177. - (BOOL)hit:(const NXPoint *)point
  178. /*
  179.  * Gets a hit if the point is within HIT_TOLERANCE of the line.
  180.  */
  181. {
  182.     NXRect r;
  183.     NXPoint p;
  184.     float lineangle, pointangle, distance;
  185.     float tolerance = HIT_TOLERANCE + linewidth;
  186.  
  187.     if (gFlags.locked || !gFlags.active) return NO;
  188.  
  189.     r = bounds;
  190.     if (r.size.width < tolerance) {
  191.     r.size.width += tolerance * 2.0;
  192.     r.origin.x -= tolerance;
  193.     }
  194.     if (r.size.height < tolerance) {
  195.     r.size.height += tolerance * 2.0;
  196.     r.origin.y -= tolerance;
  197.     }
  198.  
  199.     if (!NXMouseInRect(point, &r, NO)) return NO;
  200.  
  201.     p.x = point->x - bounds.origin.x;
  202.     p.y = point->y - bounds.origin.y;
  203.     if (gFlags.downhill) p.y = bounds.size.height - p.y;
  204.     if (p.x && bounds.size.width) {
  205.     lineangle = atan(bounds.size.height/bounds.size.width);
  206.     pointangle = atan(p.y/p.x);
  207.     distance = sqrt(p.x*p.x+p.y*p.y)*sin(fabs(lineangle-pointangle));
  208.     } else {
  209.     distance = fabs(point->x - bounds.origin.x);
  210.     }
  211.  
  212.     return((distance - tolerance) <= linewidth);
  213. }
  214.  
  215. /* Methods intended to be subclassed */
  216.  
  217. - (float)arrowAngle:(int)corner
  218. /*
  219.  * Returns the angle which the arrow should be drawn at.
  220.  */
  221. {
  222.     float angle;
  223.     angle = atan2(bounds.size.height, bounds.size.width);
  224.     angle = (angle / 3.1415) * 180.0;
  225.     switch (corner) {
  226.     case UPPER_RIGHT: return angle;
  227.     case LOWER_LEFT: return angle + 180.0;
  228.     case UPPER_LEFT: return 180.0 - angle;
  229.     case LOWER_RIGHT: return - angle;
  230.     }
  231.     return angle;
  232. }
  233.  
  234. - drawLine
  235. /*
  236.  * The actual line drawing is done here so that it can be subclassed.
  237.  */
  238. {
  239.     if (gFlags.downhill) {
  240.     PSLine(bounds.origin.x, bounds.origin.y + bounds.size.height,
  241.            bounds.size.width, - bounds.size.height);
  242.     } else {
  243.     PSLine(bounds.origin.x, bounds.origin.y,
  244.            bounds.size.width, bounds.size.height);
  245.     }
  246.     return self;
  247. }
  248.  
  249. - (BOOL)wantsLineColor
  250. {
  251.     return YES;
  252. }
  253.  
  254. - (BOOL)wantsFillColor
  255. {
  256.     return NO;
  257. }
  258.  
  259. /* Archiving methods */
  260.  
  261. - write:(NXTypedStream *)stream
  262. {
  263.     [super write:stream];
  264.     NXWriteType(stream, "i", &startCorner);
  265.     return self;
  266. }
  267.  
  268. - read:(NXTypedStream *)stream
  269. {
  270.     [super read:stream];
  271.     if (NXTypedStreamClassVersion(stream, [self name]) > 0) {
  272.     NXReadType(stream, "i", &startCorner);
  273.     } else {
  274.     startCorner = LOWER_LEFT;
  275.     }
  276.     return self;
  277. }
  278.  
  279. @end
  280.  
  281.